/* Routines for drawing MMIs with angled corners.
 *
 * Copyright (c) 2006-2007 Xaveer Leijtens
 *
 * This file is part of libMask.
 *
 * libMask is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * libMask is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with libMask.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mask.h"

#define VERYSMALL 1e-12

/* Funtion draws half a 1x MMI with specified offset, and angled corners.
 * If the current pointer points to the right, the MMI is as shown, with
 * offset 1,2 as below (zero offset is in center, positive moves in the
 * direction shown):
 *        ________
 *       /        | 
 *   ->   o  +    | (CP not changed by this function)
 *   cp  \___V____|
 *
 */
void half_1x(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg,     /* Width of input/output waveguide */
    double o,       /* Waveguide offsets (relative to MMI center) */
    double angle    /* Angle of corners: RAD(90) is normal MMI */
    )
{
  double ltap1, ltap2;
  push_cp();

  /* Angle should be between 0 and 90 degrees. First map to positive
   * angles, and if the angle is close to or larger than 90 degrees, use 90
   * degrees, otherwise, use the angle.
   */
  angle = map_0_2pi(angle);

  if (o < 0) {
    move(0,0,0,FLIP);
    o = -o;
  }
  ltap1 = RAD(90) - angle > VERYSMALL ? (wmmi/2 - wwg/2 - o) / tan(angle) : 0;
  taper(ltap1, wwg, wmmi - 2*o); /* draw the first part of the taper */
  ltap2 = RAD(90) - angle > VERYSMALL ? 2*o / tan(angle) : 0;
  if (ltap2 > VERYSMALL) {
    taper_angle(ltap2, -atan(o/ltap2), wmmi - 2*o, wmmi); /* not symmetric */
  } else {
    offset(-o);
  }
  sw(lmmi/2 - ltap1 - ltap2, wmmi);
  pop_cp();
}

/* Funtion draws a 1x1 MMI with specified offsets, and angled corners.
 * If the current pointer points to the right, the MMI is as shown, with
 * offset 1,2 as below (zero offset is in center, positive moves in the
 * direction shown):
 *          ___________________
 * w2      /                   \
 * w1  ->   1  +              2  ->
 * w0  cp  \___V_______________/ cp (after)
 *
 */
void mmi_1x1(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg,     /* Width of input/output waveguide */
    double o1,      /* Waveguide offsets (relative to MMI center) */
    double o2,      /* Waveguide offsets (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  push_cp();
/* Input side */
  half_1x(lmmi, wmmi, wwg, o1, angle);
  move(lmmi, o1-o2, RAD(180), 0);
/* Output side */
  half_1x(lmmi, wmmi, wwg, -o2, angle);
  pop_cp(); /* move to the start and skip over the MMI */
  skip(lmmi);
  offset(o2-o1);
}

/* Funtion draws a 1x2 MMI with specified offsets, and angled corners.
 * If the current pointer points to the right, the MMI is drawn from the
 * lower waveguide, offset 1,2 and 3,4 as below (positive moves in
 * direction as shown):
 *          ___________________
 *         |                   \
 *         |2      ^          3
 *        /        | -         /
 * w1  ->          | +         \
 *     cp \ 1      V          2  ->
 *         |___________________/ cp (after)
 *
 */
void mmi_1x2(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg,     /* Width of input/output waveguide */
    double o1,      /* Waveguide offset (relative to MMI center) */
    double o2,      /* Waveguide offset (relative to MMI center) */
    double o3,      /* Waveguide offset (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  mmi_2x2(lmmi, wmmi, wwg, o1, o1, o2, o3, angle);
}

/* Funtion draws a 2x2 MMI with specified offsets, and angled corners.
 * If the current pointer points to the right, the MMI is drawn from the
 * lower waveguide, offset 1,2 and 3,4 as below (positive moves in
 * direction as shown):
 *          ___________________
 * w4      /                   \
 * w3       2      ^          4
 * w2      \       | -         /
 *         /       | +         \
 * w1  ->   1      V          3  ->
 * w0  cp  \___________________/ cp (after)
 *
 */
void mmi_2x2(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg,     /* Width of input/output waveguide */
    double o1,      /* Waveguide offset (relative to MMI center) */
    double o2,      /* Waveguide offset (relative to MMI center) */
    double o3,      /* Waveguide offset (relative to MMI center) */
    double o4,      /* Waveguide offset (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  push_cp();
  offset(-o1+o2); /* 2->4 first */
  mmi_1x1(lmmi, wmmi, wwg, o2, o4, angle);
  pop_cp();
  mmi_1x1(lmmi, wmmi, wwg, o1, o3, angle);
}

/* Funtion draws a 2x2 MMI with specified offsets, and angled corners and
 * tapers at input and output.
 * If the current pointer points to the right, the MMI is drawn from the
 * lower waveguide, offset 1,2 and 3,4 as below (positive moves in
 * direction as shown):
 *          ___________________
 * w4      /                   \
 * w3       2      ^          4
 * w2      \       | -         /
 *         /       | +         \
 * w1  ->   1      V          3  ->
 * w0  cp  \___________________/ cp (after)
 *
 */
void mmi_2x2taper(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg1,    /* Width of input/output waveguide at start */
    double wwg2,    /* Width of input/output waveguide at MMI */
    double ltap,    /* Length of taper */
    double o1,      /* Waveguide offset (relative to MMI center) */
    double o2,      /* Waveguide offset (relative to MMI center) */
    double o3,      /* Waveguide offset (relative to MMI center) */
    double o4,      /* Waveguide offset (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  push_cp();
  offset(-o1+o2);
  taper(ltap, wwg1, wwg2);
  pop_cp();
  taper(ltap, wwg1, wwg2);
  mmi_2x2(lmmi, wmmi, wwg2, o1, o2, o3, o4, angle);
  push_cp();
  offset(-o3+o4);
  taper(ltap, wwg2, wwg1);
  pop_cp();
  taper(ltap, wwg2, wwg1);
}

/* Funtion draws a 1x2 MMI with specified offsets, and angled corners and
 * tapers at input and output.
 * If the current pointer points to the right, the MMI is drawn from the
 * input waveguide, offset 1 and 2,3 as below (positive moves in direction
 * as shown):
 *          ___________________
 *         |                   \
 *         |2      ^          3
 *        /        | -         /
 * w1  ->          | +         \
 *     cp \ 1      V          2  ->
 *         |___________________/ cp (after)
 *
 */
void mmi_1x2taper(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg1,    /* Width of input/output waveguide at start */
    double wwg2,    /* Width of input/output waveguide at MMI */
    double ltap,    /* Length of taper */
    double o1,      /* Waveguide offset (relative to MMI center) */
    double o2,      /* Waveguide offset (relative to MMI center) */
    double o3,      /* Waveguide offset (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  mmi_2x2taper(lmmi, wmmi, wwg1, wwg2, ltap, o1, o1, o2, o3, angle);
}

void mmi_1x1taper(
    double lmmi,    /* Length of the MMI */
    double wmmi,    /* Width of the MMI */
    double wwg1,    /* Width of input/output waveguide at start */
    double wwg2,    /* Width of input/output waveguide at MMI */
    double ltap,    /* Length of taper */
    double o1,      /* Waveguide offsets (relative to MMI center) */
    double o2,      /* Waveguide offsets (relative to MMI center) */
    double angle    /* Angle of corners */
    )
{
  taper(ltap, wwg1, wwg2);
  mmi_1x1(lmmi, wmmi, wwg2, o1, o2, angle);
  taper(ltap, wwg2, wwg1);
}
